package edu.vt.cbil.dataset;

import edu.vt.cbil.util.BioToolbox;
import org.apache.commons.math3.distribution.NormalDistribution;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


/**
 * @author Justin
 * @version 1.0
 * @created 09-11月-2014 11:58:21
 */
public final class Segment implements Comparable<Segment>, Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 4905497107751450036L;
    public Long Seg_id;
    public short Chr_id;
    public long StartLoci;
    /**
     *
     */
    public Map<Long, Double> Loci;
    public double splittingThreshold;
    public boolean isReady;
    /**
     *
     */
    public double robustMeanHalfCopyNumber;
    public double robustStdHalfCopyNumber;

    public double purityCorrCopyNumber;

    public double avgHalfCopyNumber;
    public double stdHalfCopyNumber;
    public double minHalfCopyNumber;
    public double maxHalfCopyNumber;
    public SegmentFeature fVector;
    private transient List<Long> Index;
    private transient List<Double> IntegralCN;


    public Segment() {
        //Loci = new TreeMap<Long, Double>();
        Loci = new LinkedHashMap<Long, Double>();
        fVector = null;
        IntegralCN = null;
        Index = null;
        Seg_id = 0L;
        isReady = false;
    }

    @Override
    public int compareTo(Segment o) {
        // TODO Auto-generated method stub
        return Seg_id.compareTo(o.Seg_id);
    }

    public void resetIndex() {
        Index = new ArrayList<Long>(Loci.keySet());
    }

    public Long getLoci(int pos) {
        return Index.get(pos);
    }

    public void resetIntegralCN() {
        //ArrayList<Double> arr = new ArrayList<Double>(Loci.values());
        IntegralCN = new ArrayList<Double>();

        IntegralCN.add(0.0);
        double sum = 0;

        for (double v : Loci.values()) {
            sum += v;
            IntegralCN.add(sum);
        }
    }

    public void releaseIntegralCN_Index() {
        IntegralCN = null;
        Index = null;
        //this.splittingThreshold = 0;
    }

    public void updateCharacters() {
        ArrayList<Double> arr = new ArrayList<Double>(Loci.values());

        //过滤掉孤立点
        double[] temp = BioToolbox.robustMean(arr, 64);
        robustMeanHalfCopyNumber = temp[0];
        robustStdHalfCopyNumber = temp[1];

        avgHalfCopyNumber = BioToolbox.mean(arr);
        stdHalfCopyNumber = BioToolbox.std(arr);
        double[] r = BioToolbox.minmax(arr);
        minHalfCopyNumber = r[0];
        maxHalfCopyNumber = r[1];
    }

    public void updateCharacters(double pValue) {
        this.updateCharacters();

        NormalDistribution norm = new NormalDistribution(0, 1);

        // threshold of the z-score given the p-value cutoff
        // cn.length * (cn.length - 1) is used for correcting multiple
        // testing

        //double cc = (double)s.Loci.size() * (s.Loci.size() - 1)/(MIN_SEG_LENGTH*MIN_SEG_LENGTH);
        //double cc = (double) Loci.size();
        double cc = (double) Loci.size() * (Loci.size() - 1);
        splittingThreshold = Math.abs(norm.inverseCumulativeProbability(pValue / cc));
    }


    public String getCharacterString() {
        return String.format("Seg_id = %02d:% 12d;\t Length =% 6d;\t thresh = %.3f\t robustAvg = %.4f \t robustStd = %.4f \t avg = %.4f \t std = %.4f \t range:[%.3f - %.3f]",
                Chr_id, Seg_id, Loci.size(), this.splittingThreshold,
                robustMeanHalfCopyNumber, robustStdHalfCopyNumber, avgHalfCopyNumber, stdHalfCopyNumber,
                minHalfCopyNumber, maxHalfCopyNumber);
    }


    public double calculateZ(int start, int stop) {
        if (stop < start) {
            int temp = start;
            start = stop;
            stop = temp;
        }
        int len = stop - start;

        double sum = 0;

//		ArrayList<Double> arr = new ArrayList<Double>(Loci.values());
//		for (int i = start; i<stop; i++)
//			sum += arr.get(i); //等价于下面语句
        //求和范围是Start，Stop - 1；不包括Stop
        sum = IntegralCN.get(stop) - IntegralCN.get(start);

        ///return Math.abs((sum - len*avgHalfCopyNumber)/(stdHalfCopyNumber*Math.sqrt(len)));
        return Math.abs((sum - len * robustMeanHalfCopyNumber) / (robustStdHalfCopyNumber * Math.sqrt(len)));
    }

//    public double calculateZ2(int start, int stop) {
//        if (stop < start) {
//            int temp = start;
//            start = stop;
//            stop = temp;
//        }
//        int len = stop - start;
//
//        double sum = 0;
//
//        //求和范围是Start，Stop - 1；不包括Stop
//        sum = IntegralCN.get(stop) - IntegralCN.get(start);
//
//        ///return Math.abs((sum - len*avgHalfCopyNumber)/(stdHalfCopyNumber*Math.sqrt(len)));
//        return Math.abs(Math.sqrt(sum*sum/len)/stdHalfCopyNumber);
//    }

    public Segment getSubSegment(int start, int stop) {
        if ((start == stop) || (Index == null)) {//调试用
            System.out.println("getSubSegment : Start == Stop or Index is NULL!");
            System.exit(-1);
        }

        Segment newSeg = new Segment();

        newSeg.Chr_id = this.Chr_id;
        newSeg.StartLoci = this.getLoci(start);
        newSeg.Seg_id = newSeg.StartLoci;

        List<Long> ids = Index.subList(start, stop);
        for (Long id : ids) {
            newSeg.Loci.put(id, this.Loci.get(id));
        }
        return newSeg;
    }

    public boolean compareSegmentKind(Segment seg) {
        int s11 = (this.fVector.isDeletion) ? 0x01 : 0x00;
        int s12 = (this.fVector.isNeutral) ? 0x01 : 0x00;
        int s1 = (s11 << 1) | s12;

        int s21 = (seg.fVector.isDeletion) ? 0x01 : 0x00;
        int s22 = (seg.fVector.isNeutral) ? 0x01 : 0x00;
        int s2 = (s21 << 1) | s22;

        return (s1 == s2);
    }

}

//    private double getThreshold(double pValue) {
//        NormalDistribution norm = new NormalDistribution(0, 1);
//
//        // threshold of the z-score given the p-value cutoff
//        // cn.length * (cn.length - 1) is used for correcting multiple
//        // testing
//
////		double cc = (double)s.Loci.size() * (s.Loci.size() - 1)/(MIN_SEG_LENGTH*MIN_SEG_LENGTH);
//        //double cc = (double) s.Loci.size();
//        double cc = (double) Loci.size() * (Loci.size() - 1);
//        return Math.abs(norm.inverseCumulativeProbability(pValue / cc));
//    }